
abstract class WebsocketController {
  socket: WebSocket
  constructor(wsUrl: string) {
    var socket = new WebSocket(wsUrl)
    socket.onmessage = (evt: MessageEvent) => {
      this.onMessage(evt)
    }
    this.socket = socket
  }

  sendData(data: any) {
    if(typeof data === 'object') {
      data = JSON.stringify(data)
    }
    if(this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(data)
    }
  }

  abstract onMessage(evt: MessageEvent): void
}

const ALL_POST_FIELDS = ['angle', 'throttle', 'drive_mode', 'recording', 'buttons']
type PostDriveData = {
  angle?: number
  throttle?: number
  drive_mode?: string
  recording?: boolean
  buttons?: {
    w1?: boolean
    w2?: boolean
    w3?: boolean
    w4?: boolean
    w5?: boolean
  }
}

type DriveReceivedData = PostDriveData & {
  num_records: number
}

/**
 * 小车地盘控制
 */
class ChassisController extends WebsocketController {
  state: ChassisState
  constructor() {
    super("ws://"+location.host + "/wsDrive")
    this.resetState()
  }

  onMessage(evt: MessageEvent): void {
    if(typeof evt.data === 'string') {
      var data: DriveReceivedData = JSON.parse(evt.data)
      this.state.numRecord = data.num_records
    }
  }

  postDrive(fields: string[] = []) {
    if(fields.length === 0) {
      fields = ALL_POST_FIELDS
    }
    let data: PostDriveData = {}

    fields.forEach(field => {
      switch (field) {
        case 'angle': data.angle = this.state.tele.user.angle;break;
        case 'throttle': data.throttle = this.state.tele.user.throttle;break;
        case 'drive_mode': data.drive_mode = this.state.driveMode;break;
        case 'recording': data.recording = this.state.recording;break;
        case 'buttons': data.buttons = this.state.buttons;break;
        default: console.log(`Unexpected post field: ${field}`); break;
      }
    })
    this.sendData(data)
  }

  resetState() {
    this.state = {
      tele: {
        user: {
          angle: 0,
          throttle: 0
        }
      },
      recording: false,
      driveMode: 'user',
      buttons: {
        w1: false,
        w2: false,
        w3: false,
        w4: false,
        w5: false
      },
      numRecord: 0
    }
  }
}

type ChassisState = {
  tele: {
    user: {
      angle: number
      throttle: number
    }
  }
  recording: boolean
  driveMode: 'user' | 'local' | 'local_angle'
  buttons: {
    w1: boolean
    w2: boolean
    w3: boolean
    w4: boolean
    w5: boolean
  }
  numRecord: number
}

/**
 * 步进电机控制
 */
class StepperController extends WebsocketController {
  
  constructor() {
    super("ws://"+location.host + "/wsStepperControl")

    
  }

  onMessage(evt: MessageEvent): void {
    
  }
}

/**
 * 小车驱动
 */
export default class Driver {
  
  chassisController: ChassisController
  stepperController: StepperController
  constructor() {
    this.chassisController = new ChassisController()
    this.stepperController = new StepperController()
    this.init()
  }

  init() {
    this.joystickLoop()
  }

  joystickLoop() {
    this.chassisController.postDrive()
    setTimeout(this.joystickLoop.bind(this), 100)
  }

  destroy() {
    
  }

  setThrottle(throttle: number) {
    this.chassisController.state.tele.user.throttle = throttle
  }
  setAngle(angle: number) {
    this.chassisController.state.tele.user.angle = angle
  }
  getThrottle() {
    return this.chassisController.state.tele.user.throttle
  }
  getAngle() {
    return this.chassisController.state.tele.user.angle
  }
}